#!/usr/bin/env python2
# coding:utf-8

import os
import re
import sys
import copy
import types
import errno
import getopt
import json
from yaml import load, dump

admin_path = os.path.abspath(os.path.split(os.path.realpath(__file__))[0])
sys.path.insert(0, "%s/../" % (admin_path))

from buddha.utils import Exp, _derror, _exec_pipe1, _human_readable, _human_unreadable, _exec_system
from buddha.lichbd import Lichbd

from gbase_utils import parser


class Lich_gbase():

    def __init__(self):
        self.success = 0
        self.is_ready = 0
        self.fail = 0
        self.fail_lun = []
        self.iscsi_lun = []

        self.lichfs = '/opt/fusionstack/lich/libexec/lichfs'
        self.lich_inspect = '/opt/fusionstack/lich/libexec/lich.inspect'
        self.data = parser()

        self.lichbd = Lichbd()

    def size_check(self, size):
        num = 0
        unit = ""

        m = re.match("\s*(\d+)\s*(\w+)", size)
        if m is not None:
            num = m.group(1).strip()
            unit = m.group(2).strip().upper()

        if unit.find('B') == -1:
            unit = unit + 'B'

        if int(num) <= 0 or unit not in ['B', 'KB', 'MB', 'GB', 'TB']:
            _derror('size %s was invald, please check gbase_config.json.' % size)
            exit(errno.EINVAL)

        return size

    def lun_info(self):
        lun_list = []
        iscsi_lun = []
    
        pools = self.data['pools']
        for pool in pools.items():
            p = pool[1]
            tier = p['type']
            luns = p['luns']
            for lun in luns.items():
                lun_list.append((pool[0], lun[0], self.size_check(lun[1]), tier))
        
        for l in lun_list:
            path = '/iscsi/' + l[0] + '/' + l[1]
            iscsi_lun.append(path)
            self.iscsi_lun = list(set(iscsi_lun))

        return lun_list

    def __lichbd_cmd(self, op=None, pool=None, lun=None, size=None):
        '''
        lichbd ls flash -p iscsi
        lichbd mkpool flash -p iscsi
        lichbd lspools -p iscsi
        lichbd create flash/volume1 --size 1G -p iscsi
        lichbd resize flash/volume1 --size 2G -p iscsi
        '''
        if op == 'create':
            return self.lichbd.vol_create('%s/%s' % (pool, lun), size)
        elif op == 'resize':
            return self.lichbd.vol_resize('%s/%s' % (pool, lun), size)
        elif op == 'mkpool':
            return self.lichbd.pool_create(pool)
        elif op == 'ls':
            return self.lichbd.pool_list(pool)
        elif op == 'lspools':
            return self.lichbd.list_all_pools()
        else:
            _derror('volumes only support create mkpool ls lspools, please try again.')
            exit(errno.EINVAL)

        #if op in ['create', 'resize'] :
        #    cmd = [self.lichbd, op, pool + '/' + lun, '-p', 'iscsi', "--size", size]
        #elif op == 'mkpool':
        #    cmd = [self.lichbd, op, pool, '-p', 'iscsi']
        #elif op == 'ls':
        #    cmd = [self.lichbd, op, pool, '-p', 'iscsi']
        #elif op == 'lspools':
        #    cmd = [self.lichbd, op, '-p', 'iscsi']
        #else:
        #    _derror('volumes only support create mkpool ls lspools, please try again.')
        #    exit(errno.EINVAL)

        #try:
        #    (out, err) = _exec_pipe1(cmd, 0, False)
        #    ret = _exec_system("echo $?", False, False)
        #    if ret == 0:
        #        if op in ['create', 'resize']:
        #            self.success += 1
        #            print(cmd)
        #        return out
        #except Exp, e:
        #    if e.errno == errno.EAGAIN:
        #        _derror('Resource temporarily unavailable, please try again later.')
        #        pass
        #    elif e.errno == errno.EEXIST:
        #        if op in ['create', 'resize']:
        #            self.success += 1
        #            print(cmd)
        #        _derror(str(e.err))
        #        pass
        #    else:
        #        _derror(str(e.err))
        #        exit(e.errno)

    def __lichfs_cmd(self, op=None, path=None):
        cmd = [self.lichfs, '--' + op, path]

        try:
            (out, err) = _exec_pipe1(cmd, 0, False)
            ret = _exec_system("echo $?", False, False)
            if ret == 0:
                return out
        except Exp, e:
            if e.errno == errno.EAGAIN:
                _derror('Resource temporarily unavailable, please try again later.')
                exit(e.errno)
            elif e.errno == errno.EEXIST:
                _derror(str(e.err))
                pass
            elif e.errno == errno.ENOENT:
                _derror('%s No such file or directory.' % path)
                _derror(str(e.err))
                pass
            else:
                _derror(str(e.err))
                exit(e.errno)

    def getstat(self, path):
        cmd = [self.lichfs, "--stat", path]
        try:
            (out, err) = _exec_pipe1(cmd, 0, False)
            ret = _exec_system("echo $?", False, False)
            if ret == 0:
                self.is_ready += 1
                for line in out.splitlines():
                    m = re.match("\s+Size:\s+(\d*).*", line)
                    if m is not None:
                        return int(m.group(1))
        except Exp, e:
            self.fail += 1
            self.fail_lun.append(path)
            if e.errno == errno.EAGAIN:
                _derror('%s Resource temporarily unavailable, please try again later.' % path)
                pass
            elif e.errno == errno.EEXIST:
                _derror('%s file was exist.' % path)
                pass
            elif e.errno == errno.ENOENT:
                _derror('%s No such file or directory.' % path)
                pass
            else:
                _derror(str(e.err))
                pass

    def lun_create(self, pool, lun, size):
        mkdir = 1
        touch = 1

        p = self.__lichbd_cmd(op='lspools', pool=pool)
        for l in p.splitlines():
            m = l.split(' ')[-1]
            if m == pool:
                mkdir = 0

        if not mkdir:
            vol = self.__lichbd_cmd(op='ls', pool=pool)
            for l in vol.splitlines():
                m = l.split(' ')[-1]
                if m == lun:
                    touch = 0

        if (mkdir):
            self.__lichbd_cmd(op='mkpool', pool=pool)

        if (touch):
            self.__lichbd_cmd(op='create', pool=pool, lun=lun, size=size)

        if (touch == 0 and mkdir == 0):
            self.success += 1
        
    def lun_set(self, pool, lun, op):
        num = 0

        if op in ['hdd', 'HDD', 'sas', 'SAS']:
            num = 1
        elif op in ['flash','FLASH','SSD','ssd']:
            num = 0
        else:
            _derror('disk type %s is invald.' % (op))
            exit(errno.EINVAL)

        path = '/iscsi/' + pool + '/' + lun 
        cmd = [self.lich_inspect, '--priority', path, str(num)]
        try:
            (out, err) = _exec_pipe1(cmd, 0, False)
            ret = _exec_system("echo $?", False, False)
            if ret == 0:
                self.success += 1
                print('set %s %s tier%d successful' % (op, lun, num))
        except Exp, e:
            if e.errno == errno.ENOSPC:
                _derror('There is no tier%d disk.' % (num))
                pass
            else:
                _derror(str(e.err))
                exit(e.errno)

    def init(self):
        lun = self.lun_info()
        count = len(lun)
        print(self.iscsi_lun)

        for l in lun:
            self.lun_create(l[0], l[1], l[2])

        if self.success != count:
            _derror('init create lun was not totally success, please try init again.')
            exit(errno.EAGAIN)

        self.success = 0
        for l in lun:
            self.lun_set(l[0], l[1], l[3])

        if self.success != count:
            _derror('init priority was not totally success, please try init again.')
            exit(errno.EAGAIN)

    def resize(self):
        lun = self.lun_info()
        count = 0

        for l in lun:
            lich_size = self.getstat(path='/iscsi/'+ l[0] + '/' + l[1])
            if lich_size is not None and (_human_unreadable(l[2], True) > float(lich_size)):
                count += 1
                self.__lichbd_cmd(op='resize', pool=l[0], lun=l[1], size=l[2])

        if self.success != count:
            _derror('resize was not totally success.')

    def ready(self):
        lun = self.lun_info()

        for l in self.iscsi_lun:
            self.getstat(l)

        print('success lun : %d' % (self.is_ready))
        print('fail lun : %d' % (self.fail))

        fail_lun = list(set(self.fail_lun))
        if len(fail_lun):
            print('fail lun : ')
            for l in fail_lun:
                print('\t%s' % l)
            exit(errno.EAGAIN)
        else:
            print('the cluster is ready. ')

    
def usage():
    print ("usage:")
    print (sys.argv[0] + " init")
    print (sys.argv[0] + " resize")
    print (sys.argv[0] + " ready")
    exit(1)

def main():
    op = ''
    ext = None
    verbose = 0

    try:
        opts, args = getopt.getopt(
                sys.argv[1:], 
                'hv', ['init', 'help', 'verbose', 'resize', 'ready']
                )
    except getopt.GetoptError, err:
        print str(err)
        usage()
    
    for o in args:
        if o in ('help'):
            usage()
            exit(0)
        elif o == 'init':
            op = o
        elif o == 'resize':
            op = o
        elif o == 'ready':
            op = o
        else:
            _derror('option %s was not allow.' % o)
            usage()
            exit(1)

    lich_gbase = Lich_gbase()
    if (op == 'init'):
        lich_gbase.init()
    elif (op == 'resize'):
        lich_gbase.resize()
    elif (op == 'ready'):
        lich_gbase.ready()
    else:
        _derror('option %s was not allow.' % o)
        usage()
        exit(1)


if __name__ == '__main__':
    if (len(sys.argv) == 1):
        usage()
    else:
        main()
